package com.ruiyi.adrobot.mongo.device.impl;

import com.ruiyi.adrobot.beans.Device;
import com.ruiyi.adrobot.beans.Model;
import com.ruiyi.adrobot.beans.VirtualDevice;
import com.ruiyi.adrobot.mongo.device.DeviceOperations;
import com.ruiyi.adrobot.utils.DateUtil;
import com.ruiyi.adrobot.utils.IMEIUtil;
import com.ruiyi.adrobot.utils.RandomUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoOperations;
import org.springframework.data.mongodb.core.aggregation.Aggregation;
import org.springframework.data.mongodb.core.aggregation.AggregationResults;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;

import java.util.List;
import java.util.Random;
import java.util.regex.Pattern;

import static org.springframework.data.mongodb.core.aggregation.Aggregation.newAggregation;
import static org.springframework.data.mongodb.core.query.Criteria.where;

/**
 * Created by Lenovo on 2017/5/9.
 */
public class DeviceRepositoryImpl implements DeviceOperations {
    @Autowired
    private MongoOperations mongo;

    @Override
    public void insertAll(List<Device> devices) {
        mongo.insertAll(devices);
    }

    @Override
    public void buildModels() {
        Aggregation agg = newAggregation(
                Aggregation.group("model", "brand", "displayId", "versionAndroid", "resolution", "dpi", "osApi").count().as("count")
        );
        List<Model> models = mongo.aggregate(agg, Device.class, Model.class).getMappedResults();
        mongo.insertAll(models);
    }

    @Override
    public Device randomFindDeviceByAccountInfosFunNot(Integer fun) {
        Query query = new Query(where("accountInfos.fun").ne(fun));
        Long count = mongo.count(query, Device.class);
        query.skip(RandomUtil.nextInt(count.intValue()));
        return mongo.findOne(query, Device.class);
    }


    @Override
    public void insertVirtualDevice(Device device) {
        VirtualDevice virtualDevice = new VirtualDevice();
        String imei = device.getImei().substring(0, 8);
        for (int i = 0; i < 100; i++) {
            try {
                virtualDevice.setImei(IMEIUtil.buildIMEIBy8(imei));
                if (null != mongo.findOne(new Query(where("_id").is(virtualDevice.getImei())), Device.class)) {
                    continue;
                }
                virtualDevice.setCreateDate(DateUtil.getCurrentTime());
                mongo.insert(virtualDevice, imei);
            } catch (Exception e) {
                continue;
            }
        }

    }

    @Override
    public Device findRandomDeviceByModelAndUsedFunsNot(String model, Integer fun) {
        Query query = new Query();
        if (null != model) {
            query.addCriteria(where("model").is(model));
        }
        if (null != fun) {
            query.addCriteria(where("usedFuns").ne(fun));
        }
        List<Device> devices = mongo.find(query, Device.class);
        if (devices.size() == 0) {
            return null;
        }
        return devices.get(RandomUtil.nextInt(devices.size()));
    }

    @Override
    public VirtualDevice findRandomDeviceByAccountInfosFunAndAccountInfosCreateDateBetween(Integer fun, String startDate, String endDate, String collection) {
        Aggregation agg = newAggregation(
                Aggregation.unwind("accountInfos"),
                Aggregation.match(Criteria.where("accountInfos.fun").is(fun).and("createDate").gt(startDate).lt(endDate))
        );
        AggregationResults<VirtualDevice> outputType = mongo.aggregate(agg, collection, VirtualDevice.class);
        List<VirtualDevice> list = outputType.getMappedResults();
        return list.get(RandomUtil.nextInt(list.size()));
    }

    @Override
    public Device findRandomDeviceByModelAndAccountInfosFunAndAccountInfosCreateDateBetween(String model, Integer fun, String startDate, String endDate) {
        Aggregation agg = newAggregation(
                Aggregation.match(Criteria.where("model").is(model)),
                Aggregation.unwind("accountInfos"),
                Aggregation.match(Criteria.where("accountInfos.fun").is(fun).and("createDate").gt(startDate).lt(endDate))
        );
        AggregationResults<Device> outputType = mongo.aggregate(agg, "device", Device.class);
        List<Device> list = outputType.getMappedResults();
        return list.get(RandomUtil.nextInt(list.size()));
    }

    @Override
    public VirtualDevice findVirtualDeviceByImei(String imei) {
        Query query = new Query(Criteria.where("_id").is(imei));
        return mongo.findOne(query, VirtualDevice.class, imei.substring(0, 8));
    }

    @Override
    public Device findRegexImei(String imei) {
        Pattern pattern = Pattern.compile("^" + imei.substring(0, 8) + ".*$", Pattern.CASE_INSENSITIVE);
        Query query = new Query(Criteria.where("_id").regex(pattern));
        return mongo.findOne(query, Device.class);
    }

    @Override
    public VirtualDevice findRandomNotUsedVirtualDevice(Integer fun, String collection) {
        Aggregation agg = newAggregation(
                Aggregation.match(Criteria.where("usedFuns").ne(fun))
        );
        List<VirtualDevice> virtualDevices = mongo.aggregate(agg, collection, VirtualDevice.class).getMappedResults();
        if (virtualDevices.size() > 0) {
            return virtualDevices.get(RandomUtil.nextInt(virtualDevices.size()));
        } else {
            return null;
        }

    }

    @Override
    public Device findRandomModel() {
        List<Device> devices = mongo.findAll(Device.class);
        Random random = new Random();
        int num = random.nextInt(devices.size());
        return devices.get(num);
    }

    @Override
    public void saveOrUpdateVirtualDeviceAccounts(VirtualDevice virtualDevice) {
        mongo.save(virtualDevice, virtualDevice.getImei().substring(0, 8));
    }

    @Override
    public void updateProvinceIdAndCityId(Device excuteDevice, Integer provinceId, Integer cityId) {
        Device device = mongo.findById(excuteDevice.getImei(), Device.class);
        if (null != device) {
            device.setProvinceId(provinceId);
            device.setCityId(cityId);
            mongo.save(device);
        } else {
            VirtualDevice virtualDevice = mongo.findById(excuteDevice.getImei(), VirtualDevice.class, excuteDevice.getImei().substring(0, 8));
            virtualDevice.setProvinceId(provinceId);
            virtualDevice.setCityId(cityId);
            mongo.save(virtualDevice, excuteDevice.getImei().substring(0, 8));
        }
    }

    @Override
    public void removeUsedFunsById(String imei, Integer fun, Boolean virtual) {
        Query query = new Query(Criteria.where("_id").is(imei));
        Update update = new Update();
        update.pull("usedFuns", fun);
        if (virtual) {
            mongo.updateFirst(query, update, imei.substring(0, 8));
        } else {
            mongo.updateFirst(query, update, Device.class);
        }
    }

}
